// ==UserScript==
// @name           IDS TEST SUITE
// @namespace      http://wasjournal.blogspot.com/IDSTESTSUITE
// @description    Helps in false positive/negative tests and regression tests of the IDS
// @include        *
// ==/UserScript==

// Author: Kishor D.
// Blog:     http://wasjournal.blogspot.com/

//
// This method is called when the user selects test option from the menu list
// It asks several interview questions and then fetches the vector file
//
window.IDSTS_Init = function ()
{
  var lh = location.href;
  var URLNoQuery = lh.indexOf('?')>=0?lh.substring(0, lh.indexOf('?')):lh;
  var Confirm = confirm("I am about to attack " + location.host + "\n" +
                        "URL I'm about to attack is : " + URLNoQuery + "\n" +
                        "Are you sure you want to proceed?");
  if(Confirm)
  {
  
    var UseDefaultFiles = confirm("Use default test files? I.e.\n" + 
	                              "http://mario.heideri.ch/xss.xml\n" + 
								  "http://ha.ckers.org/xssAttacks.xml\n" + 
								  "http://xssdb.dabbledb.com/publish/xssdb/d8266d7f-397e-4162-ad9f-5556494f18e9/xssdbtestview01.rss");
	
	window.IDSTS_VectorFiles = [];
	window.IDSTS_VectorFilesIdx = 0;
	var vf = window.IDSTS_VectorFiles;
	if(UseDefaultFiles)
	{
	  vf.push({url:"http://mario.heideri.ch/xss.xml", 
	           xpath: '//code'
			  });
	  vf.push({url:"http://ha.ckers.org/xssAttacks.xml", 
	           xpath: '//code'
			  });
	  vf.push({url:"http://xssdb.dabbledb.com/publish/xssdb/d8266d7f-397e-4162-ad9f-5556494f18e9/xssdbtestview01.rss", 
	           xpath: "//ddb:exploitString",
			  });
	}
	else
	{
      var VectorFile = "http://mario.heideri.ch/xss.xml";
	  var UserVectorFile = 
	        prompt ("Please supply the URL of the XML file containing test vectors" + 
	                "\n Or leave the text box blank to use " + VectorFile);
	
	  if(UserVectorFile != "")
	    VectorFile = UserVectorFile;

	  vf.push({url:"http://mario.heideri.ch/xss.xml", 
	           xpath: '//code'
			  });
	}
	
	window.IDSTS_TestPostParameter = confirm("Do you wish to run POST test on the URL?");
	window.IDSTS_IDSTest = confirm("Press Yes for IDS test, CANCEL for false +ve test");
	
	window.IDSTS_URLToAttack = URLNoQuery;
	window.IDSTS_TempVectorContainer = [];
	
	if(window.IDSTS_VectorFiles.length > window.IDSTS_VectorFilesIdx)
	{
	  var VFile = window.IDSTS_VectorFiles[window.IDSTS_VectorFilesIdx];
	  window.IDSTS_VectorFilesIdx++;
      window.IDSTS_FetchFileContent(VFile.url, window.IDSTS_ProcessVectorFile);
	}
  }
}

//
// this method does a xml http request and supplies the content to the Callback function
//
window.IDSTS_FetchFileContent = function (File, CallBack, Method, PostData, Headers)
{
  if (!Method)
    Method = "GET";
  if(!Headers)
    Headers = [];

  GM_xmlhttpRequest(
   {
     method: Method,
     url: File,
     headers: Headers,
     onload: CallBack,
	 data: PostData
   }
  );
}

//
// Vector file fetched from  init method is received here
//
window.IDSTS_ProcessVectorFile = function (ResponseStatus)
{
  if(ResponseStatus.status.toString() == "200")
  {
    var Vectors = window.IDSTS_TempVectorContainer;
	var ThisVFile = window.IDSTS_VectorFiles[window.IDSTS_VectorFilesIdx-1];// incremented earlier
    window.IDSTS_ExtractTestVectors(ResponseStatus.responseText, ThisVFile.xpath, Vectors);

    if(window.IDSTS_VectorFiles.length > window.IDSTS_VectorFilesIdx)
	{
	  var NextVFile = window.IDSTS_VectorFiles[window.IDSTS_VectorFilesIdx];
	  window.IDSTS_VectorFilesIdx++;
      window.IDSTS_FetchFileContent(NextVFile.url, window.IDSTS_ProcessVectorFile);
	  return; // when all vectors are fetched, the code below executes
	}
	
	window.IDSTS_InitProgressBar(Vectors.length);
	window.IDSTS_RunTest(Vectors);
  }
}

//
// This method parses the xml content and executes xpath on it.
// It then recursively traverses the result and returns an array of injection objects
// TODO: to make it more flexible, this method should not be aware of GET, POSt and stuff
// All it should do is get the list of vectors
//
window.IDSTS_ExtractTestVectors = function (XmlContent, XPath, Vectors)
{
  var Parser = new DOMParser();
  if(!Parser)
  {
    return Vectors;
  }
  
  var XmlDoc = Parser.parseFromString(XmlContent, "text/xml");
  if(!XmlDoc)
  {
    return Vectors;
  }

  var VectorNodes = XmlDoc.evaluate(XPath, XmlDoc, window.IDSTS_NSResolver, XPathResult.ANY_TYPE, null);
  var Node;

  function trim(s){if(!s)return s; return s.replace(/^\s/m, '').replace(/\s$/m);};
  
  while((Node = VectorNodes.iterateNext()))
  {
    var Content = trim(Node.textContent);
	if(Content && Content != "")
	{
      Vectors.push( {Method: 'GET', ParamName: 'test', AttackString : Node.textContent} );
	  if(window.IDSTS_TestPostParameter)
	    Vectors.push( {Method: 'POST', ParamName: 'test', AttackString : Node.textContent} );
	}
  }
  window.IDSTS_log_handler('\nTotal ' + Vectors.length + ' Vectors');
}

//
// Here the injection process is started after initializing global injection info properties
//
window.IDSTS_RunTest = function (Vectors)
{
  window.IDSTS_VectorInfo = {'Vectors': Vectors, CurrentVectorIndex: 0};
  window.IDSTS_FireVectorSet(1);
}

//
// Due to limitation of GM xmlhttp, currently only one request is made at a time
//
window.IDSTS_FireVectorSet = function (nbrParallelRequests)
{
  var Vectors = window.IDSTS_VectorInfo.Vectors;
  var CurrentVectorIndex = window.IDSTS_VectorInfo.CurrentVectorIndex;
  var URLToAttack;
  var CurrentVectorInfo;
  var Query = "";
  var ParamName, ParamValue, Method;
  for(var i=CurrentVectorIndex ;i<CurrentVectorIndex+nbrParallelRequests; i++)
  {
    window.IDSTS_VectorInfo.CurrentVectorIndex++;
    URLToAttack = window.IDSTS_URLToAttack;
    CurrentVectorInfo = Vectors[i];
	
	if(!CurrentVectorInfo)
	  continue;
	
	ParamName = CurrentVectorInfo.ParamName;
	ParamValue = CurrentVectorInfo.AttackString;
	Method = CurrentVectorInfo.Method;
	Headers = {'Content-type' : 'application/x-www-form-urlencoded'};

	//if(Method == 'GET')
	{
	  ParamValue = escape(ParamValue);
	}

	ParamValue = ParamValue.replace(/=/gm, '%3D').replace(/&/gm, '%26');
	Query = escape(ParamName) + "=" + ParamValue;
	
	if(Method == 'GET')
	{
	  URLToAttack = window.IDSTS_URLToAttack + "?" + Query;
	  Query = void(0);
	  Headers = [];
	}
	
	window.IDSTS_FetchFileContent(URLToAttack, window.IDSTS_MatchResponse, Method, Query, Headers);
  }
}

//
// Here, the attack response is received. We call FireVectorSet method so that
// it injects the remaining vectors if any. this hack is needed because of the asynch
// mechanism
//
window.IDSTS_MatchResponse = function (ResponseDetails)
{
  var StringsToMatch = ['found injection:'];
  
  if(ResponseDetails.status.toString() == '200')
  {
    var ResponseText = ResponseDetails.responseText;
	var Found = false;
	var MatchString;
	for(var i=0; i<StringsToMatch.length; i++)
	{
	  MatchString = StringsToMatch[i];
	  if(ResponseText.indexOf(MatchString) >= 0)
	    Found = true;
	}
	
	if(!Found && window.IDSTS_IDSTest)
	{
      window.IDSTS_log_handler("\nIDS Failed ! \n Injection = " + window.IDSTS_GetCurrentTestString());  
	}
	else if(Found && !window.IDSTS_IDSTest)
	{
      window.IDSTS_log_handler("\nFalse Positive Detected ! \n Injection = " + window.IDSTS_GetCurrentTestString());  
	}
  }
  else
  {
    window.IDSTS_log_handler("\nResponse Status : " + ResponseDetails.status.toString() + "\n" +
	         "For string : " + window.IDSTS_GetCurrentTestString());
  }
  
  window.IDSTS_RegisterProgress();
  
  window.IDSTS_FireVectorSet(1);
}

//
// Currently, since only one injection happens at a time, we can reverse map
// the injection string from injection response
//
window.IDSTS_GetCurrentTestString = function ()
{
  var Vectors = window.IDSTS_VectorInfo.Vectors;
  var CurrentVectorIndex = window.IDSTS_VectorInfo.CurrentVectorIndex - 1; // incremented before injection
  var String;
  
  if(Vectors[CurrentVectorIndex])
    String = Vectors[CurrentVectorIndex].Method + ":" 
	          + Vectors[CurrentVectorIndex].AttackString;
	
  return String;
}

window.IDSTS_InitProgressBar = function(nbrSamples)
{
  var EmptyDiv = document.createElement('div');
  EmptyDiv.id = 'IDSTS_empty';
  EmptyDiv.style.backgroundColor = '#fbfbfb';
  EmptyDiv.style.position = 'absolute';
  EmptyDiv.style.left = '10%'; 
  EmptyDiv.style.top = '10%';
  EmptyDiv.style.border = '2px solid black';
  EmptyDiv.style.height = '5%';
  EmptyDiv.style.width = '80%';
  EmptyDiv.style.padding = '.5%';
  EmptyDiv.style.align = 'left';
  var ProgressBarDivs = '<div id="IDSTS_d1" style="position:absolute;top:12.5%;left:49%;'+
					'color:#333;height:100%;text-align:center;font:bold;'+
					'padding:0px;padding-top:5px;z-index:100;">'+
					'</div><div id="IDSTS_d2" style="position:relative;top:0px;left:0px;'+
                    'background-color:#73bf00;height:100%;width:0px;'+
					'padding-top:5px;padding:0px;">'+
					'</div>';
  EmptyDiv.innerHTML = ProgressBarDivs;
  document.body.appendChild(EmptyDiv);
  document.getElementById("IDSTS_d1").innerHTML="";
  document.getElementById("IDSTS_d2").style.width=0;
  window.IDSTS_AttackProgress=0;
  window.IDSTS_nbrSamples = nbrSamples;
  window.IDSTS_RegisterProgress();
}
window.IDSTS_RegisterProgress = function()
{
   var percent;
   if (window.IDSTS_AttackProgress<window.IDSTS_nbrSamples)
   {
      percent = (100 * window.IDSTS_AttackProgress) / window.IDSTS_nbrSamples;
      document.getElementById("IDSTS_d1").innerHTML=parseInt(percent)+"% Progress";
      document.getElementById("IDSTS_d2").style.width=(100*percent)/100+"%";
      window.IDSTS_AttackProgress++;
   }
   else
   {
    if(typeof(console.log) == 'function') {
      alert('Test Complete! See Firebug Console for Details');
    } else {      
      alert('Test Complete! See Error Console for Details');
    } 
     var e=document.getElementById("IDSTS_empty");
	 document.body.removeChild(e);
   }
}


// Detects if correct version of Greasemonkey is present
window.IDSTS_DetectGMSupport = function()
{
  try
  {
    var result = (!GM_setValue || !GM_getValue || !GM_registerMenuCommand);
  }
  catch(e)
  {
    return false;
  }
  return true;
}

window.IDSTS_main = function()
{
  if(window.IDSTS_DetectGMSupport())
  {
    GM_registerMenuCommand("Run IDS Tests", window.IDSTS_Init);
  }
  window.IDSTS_VectorSet = []; // this member holds all test vectors and other associated info
}

window.IDSTS_main();

window.IDSTS_log_handler = function(string)
{
  if(typeof(console.log) == 'function') {
    console.info(string);
  } else {      
    GM_log(string);
  }        
}

window.IDSTS_NSResolver = function(prefix)
{
    if (prefix=="ddb")
        return "http://dabbledb.com/schema";

    return null;
}
